/**
 * Copyright (c) 2015-2030 by Beijing Yanlight Co., Ltd.
 * All rights reserved.
 */
package com.yanlight.app.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.yanlight.Constants;
import com.yanlight.app.dal.dao.OrderDAO;
import com.yanlight.app.request.OrderPlaceRequest;
import com.yanlight.app.request.OrderPrePayRequest;
import com.yanlight.app.request.OrderUnifiedPlaceRequest;
import com.yanlight.app.response.OrderPlaceResponse;
import com.yanlight.app.response.OrderPrePayResponse;
import com.yanlight.app.response.OrderUnifiedPlaceResponse;
import com.yanlight.app.service.AlipayService;
import com.yanlight.app.service.CompanyService;
import com.yanlight.app.service.OrderService;
import com.yanlight.exception.ErrorType;
import com.yanlight.exception.TradeException;
import com.yanlight.support.BeanContext;
import com.yanlight.support.dev.order.notify.DevOrderNotifyEvent;
import com.yanlight.support.pay.PayFactory;
import com.yanlight.support.pay.PayNotifyRequest;
import com.yanlight.app.dal.dto.OrderPayment;
import com.yanlight.support.event.OrderPayNotifyEvent;
import com.yanlight.app.dal.entity.Company;
import com.yanlight.app.dal.entity.Order;
import com.yanlight.app.service.PayService;
import com.yanlight.app.service.WxpayService;
import com.yanlight.type.OrderPreErrorType;
import com.yanlight.type.OrderSceneType;
import com.yanlight.type.OrderType;
import com.yanlight.type.PayType;
import com.yanlight.util.DateTimeUtil;
import com.yanlight.util.ToolsUtil;
import com.yanlight.util.WebUtil;

/**
 * 
 * @author haolw
 * @time 2017/12/07
 */
@Service
public class OrderServiceImpl implements OrderService {
	@Autowired
	private OrderDAO orderDAO;
	@Autowired
	private CompanyService companyService;
	@Autowired
	private WxpayService wxpayService;
	@Autowired
	private AlipayService alipayService;

	@Override
	@Transactional(rollbackFor = Exception.class)
	public OrderPlaceResponse place(OrderPlaceRequest request) {
		OrderPlaceResponse response = new OrderPlaceResponse();
		String sn = ToolsUtil.createOrderNo();
		String outTradeNo = ToolsUtil.uuid();
		Integer companyId = request.getCompanyId();
		Integer payType = request.getPayType();
		Integer payCodeId = request.getPayCodeId();
		Integer operationId = request.getOperationId();
		String remark = request.getRemark();
		Company company = companyService.findById(companyId);
		if (company == null) {
			throw TradeException.error(ErrorType.INVALID_COMPANY);
		} else {
			if (payCodeId == null) {
				throw TradeException.error(ErrorType.INVALID_PAYCODE);
			}
			PayService payService = PayFactory.getPayService(payType);
			String mchUserId = payService.getMchUserId(companyId);
			if (payService.validate(request)) {
				Order order = new Order();
				order.setSn(sn);
				order.setCompanyId(companyId);
				order.setMchId(mchUserId);
				order.setOpenId(request.getBuyerId());
				order.setTotalFee(NumberUtils.createDouble(request.getTotalFee()));
				order.setStatus(OrderType.UNPAID.getType());
				order.setCreateTime(DateTimeUtil.getNowTime());
				order.setPayCodeId(payCodeId);
				order.setOperationId(operationId);
				order.setOutTradeNo(outTradeNo);
				order.setScene(request.getSceneType());
				order.setRemark(remark);
				orderDAO.save(order);
				request.setSn(sn);
				request.setMchId(mchUserId);
				request.setOutTradeNo(outTradeNo);
				request.setCompanyName(company.getName());
				request.setCompanyShortName(company.getShortName());
				response = payService.placeOrder(request);
			}
			return response;
		}
	}

	@Override
	public OrderUnifiedPlaceResponse unifiedPlace(OrderUnifiedPlaceRequest request) {
		String outTradeNo = request.getOutTradeNo();
		Integer totalFee = request.getTotalFee();
		String notifyUrl = request.getNotifyUrl();
		Integer companyId = request.getCompanyId();
		Integer operationId = request.getOperationId();
		String remark = request.getRemark();
		if (StringUtils.isBlank(outTradeNo)) {
			throw TradeException.error(ErrorType.INCOMPLETE_PARAMETERS, "outTradeNo");
		}
		if (totalFee == null) {
			throw TradeException.error(ErrorType.INCOMPLETE_PARAMETERS, "totalFee");
		}
		if (StringUtils.isBlank(notifyUrl)) {
			throw TradeException.error(ErrorType.INCOMPLETE_PARAMETERS, "notifyUrl");
		} else {
			if (WebUtil.checkLocalUrl(notifyUrl)) {
				throw TradeException.error(ErrorType.UNLAWFUL_PARAMETERS, "notifyUrl");
			}
		}
		if (!wxpayService.checkConfig(companyId) && !alipayService.checkConfig(companyId)) {
			throw TradeException.error(ErrorType.NO_SUPPORT_PAYMENT);
		}
		Order storageOrder = orderDAO.findByOutTradeNo(outTradeNo);
		if (storageOrder != null) {
			throw TradeException.error(ErrorType.REPEATED_DATA, "订单号重复");
		}
		OrderUnifiedPlaceResponse response = new OrderUnifiedPlaceResponse();
		String sn = ToolsUtil.createOrderNo();
		Order order = new Order();
		order.setSn(sn);
		order.setCompanyId(companyId);
		order.setOperationId(operationId);
		order.setTotalFee(ToolsUtil.convertFee(request.getTotalFee()));
		order.setStatus(OrderType.UNPAID.getType());
		order.setCreateTime(DateTimeUtil.getNowTime());
		order.setOutTradeNo(outTradeNo);
		order.setScene(OrderSceneType.DEVELOPER_UNIFIED.getType());
		order.setNotifyUrl(notifyUrl);
		order.setRemark(remark);
		orderDAO.save(order);
		String paymentUrl = Constants.DEV_PAYMENT_URL.concat(outTradeNo);
		response.setPaymentUrl(paymentUrl);
		return response;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public OrderPrePayResponse prePay(OrderPrePayRequest request) {
		OrderPrePayResponse response = new OrderPrePayResponse();
		String outTradeNo = request.getOutTradeNo();
		Integer payType = request.getPayType();
		String buyerId = request.getBuyerId();
		String spbillCreateIp = request.getSpbillCreateIp();
		Order preOrder = orderDAO.findByOutTradeNo(outTradeNo);
		if (preOrder == null) {
			response.setStatus(OrderPreErrorType.NO_ORDER.getType());
			return response;
		}
		
		String sn = preOrder.getSn();
		Integer companyId = preOrder.getCompanyId();
		PayService payService = PayFactory.getPayService(payType);
		if (!payService.checkConfig(companyId)) {
			if (PayType.ALIPAY.getType().equals(payType)) {
				response.setStatus(OrderPreErrorType.NO_SUPPORT_ALIPAY.getType());
			} else if (PayType.WXPAY.getType().equals(payType)) {
				response.setStatus(OrderPreErrorType.NO_SUPPORT_WXPAY.getType());
			}
			return response;
		}
		Company company = companyService.findById(companyId);
		String mchUserId = payService.getMchUserId(companyId);
		OrderPlaceRequest placeRequest = new OrderPlaceRequest();
		placeRequest.setSn(sn);
		placeRequest.setCompanyId(companyId);
		placeRequest.setMchId(mchUserId);
		placeRequest.setOutTradeNo(outTradeNo);
		placeRequest.setCompanyName(company.getName());
		placeRequest.setCompanyShortName(company.getShortName());
		placeRequest.setBuyerId(buyerId);
		placeRequest.setSpbillCreateIp(spbillCreateIp);
		placeRequest.setTotalFee(String.valueOf(preOrder.getTotalFee()));
		orderDAO.updateMchId(preOrder.getId(), mchUserId);
		OrderPlaceResponse placeResponse = payService.placeOrder(placeRequest);
		response.setPayType(payType);
		response.setOutTradeNo(outTradeNo);
		response.setPayData(placeResponse);
		response.setStatus(OrderPreErrorType.SUCCESS.getType());
		return response;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean processPayNotify(PayNotifyRequest request) {
		String openid = request.getBuyerId();
		String transactionNo = request.getTransactionNo();
		String outTradeNo = request.getOutTradeNo();
		String payTime = request.getPayTime();
		Integer payType = request.getPayType();
		Order order = orderDAO.findByOutTradeNo(outTradeNo);
		if (order != null) {
			String notifyUrl = order.getNotifyUrl();
			OrderPayment orderPayed = new OrderPayment();
			orderPayed.setPayTime(payTime);
			orderPayed.setOpenId(openid);
			orderPayed.setPayType(payType);
			orderPayed.setTransactionNo(transactionNo);
			orderPayed.setOutTradeNo(outTradeNo);
			orderDAO.updatePayment(orderPayed);
			if (StringUtils.isNotBlank(notifyUrl)) {
				DevOrderNotifyEvent devEvent = new DevOrderNotifyEvent();
				devEvent.setNotifyUrl(order.getNotifyUrl());
				devEvent.setOutTradeNo(order.getOutTradeNo());
				devEvent.setPayTime(payTime);
				devEvent.setPayType(payType);
				devEvent.setTotalFee(ToolsUtil.convertFee(order.getTotalFee()));
				devEvent.setOperationId(order.getOperationId());
				BeanContext.publishEvent(devEvent);
			}
			OrderPayNotifyEvent event = new OrderPayNotifyEvent();
			event.setOrderId(order.getId());
			event.setCompanyId(order.getCompanyId());
			event.setPayType(payType);
			event.setSceneType(order.getScene());
			event.setPayTime(payTime);
			event.setFee(order.getTotalFee());
			event.setUserId(order.getOperationId());
			BeanContext.publishEvent(event);
		}
		return true;
	}

}
